import { _decorator, Component, Node, Vec3 } from 'cc';
import { ExtractPoint } from './ExtractPoint';
import ChunkManager, { CHUNK_SIZE } from '../world/ChunkManager';
import { GlobalEventBus } from '../core/EventBus';

const { ccclass, property } = _decorator;

type ExtractKind = 'fixed' | 'hidden';

interface ExtractData {
  node: Node;
  pos: Vec3;
  radius: number;
  kind: ExtractKind;
  triggered: boolean;
  revealed: boolean;
}

@ccclass('ExtractSpawner')
export class ExtractSpawner extends Component {
  @property(Node)
  public worldLayer: Node | null = null;

  @property(Node)
  public player: Node | null = null;

  private extracts: ExtractData[] = [];
  private chunkMgr: ChunkManager | null = null;

  onEnable() {
    this.chunkMgr = this.node.getComponent(ChunkManager) ?? this.node.parent?.getComponent(ChunkManager) ?? null;
    this.unschedule(this._poll);
    this.schedule(this._poll, 0.1);
  }

  onDisable() {
    this.unschedule(this._poll);
  }

  public spawnFixed(pos: Vec3, radius = 40): Node | null {
    const root = this.worldLayer ?? this.node;
    const n = ExtractPoint.spawnAt(root, pos.clone());
    n.active = true;
    const data: ExtractData = {
      node: n,
      pos: pos.clone(),
      radius,
      kind: 'fixed',
      triggered: false,
      revealed: true,
    };
    this.extracts.push(data);
    return n;
  }

  public spawnHidden(randomInLoadedChunks = true, radius = 40): Node | null {
    const root = this.worldLayer ?? this.node;
    const pos = this.pickHiddenPosition(randomInLoadedChunks);
    const n = ExtractPoint.spawnAt(root, pos.clone());
    n.active = false; // keep hidden until discovered
    const data: ExtractData = {
      node: n,
      pos: pos.clone(),
      radius,
      kind: 'hidden',
      triggered: false,
      revealed: false,
    };
    this.extracts.push(data);
    return n;
  }

  public registerExisting(node: Node, radius = 40, kind: ExtractKind = 'fixed', revealed = true): void {
    const data: ExtractData = {
      node,
      pos: node.worldPosition.clone(),
      radius,
      kind,
      triggered: false,
      revealed,
    };
    if (kind === 'hidden' && !revealed) {
      node.active = false;
    }
    this.extracts.push(data);
  }

  private pickHiddenPosition(randomInLoadedChunks: boolean): Vec3 {
    const result = new Vec3();
    const playerPos = this.player?.worldPosition ?? new Vec3(0, 0, 0);
    const pcx = Math.floor(playerPos.x / CHUNK_SIZE);
    const pcy = Math.floor(playerPos.y / CHUNK_SIZE);
    if (randomInLoadedChunks && this.chunkMgr) {
      const keys = this.chunkMgr.getLoadedKeys();
      const candidates = keys
        .map((k) => k.split(',').map((s) => parseInt(s, 10)) as [number, number])
        .filter(([cx, cy]) => !(cx === pcx && cy === pcy));
      if (candidates.length > 0) {
        const [cx, cy] = candidates[Math.floor(Math.random() * candidates.length)];
        const margin = 0.2;
        const span = 1 - margin * 2;
        const rx = margin + Math.random() * span;
        const ry = margin + Math.random() * span;
        result.x = cx * CHUNK_SIZE + rx * CHUNK_SIZE;
        result.y = cy * CHUNK_SIZE + ry * CHUNK_SIZE;
        return result;
      }
    }
    // Fallback: place ahead of player on +X axis
    result.x = (pcx + 1) * CHUNK_SIZE + CHUNK_SIZE * 0.5;
    result.y = pcy * CHUNK_SIZE + CHUNK_SIZE * 0.5;
    return result;
  }

  public getExtractPositions(): Vec3[] {
    return this.extracts
      .filter((e) => e.revealed)
      .map((e) => e.pos.clone());
  }

  public getRevealedExtracts(): Array<{ pos: Vec3; kind: ExtractKind }> {
    return this.extracts
      .filter((e) => e.revealed)
      .map((e) => ({ pos: e.pos.clone(), kind: e.kind }));
  }

  private _poll = () => {
    if (!this.player) return;
    const playerPos = this.player.worldPosition;
    for (const e of this.extracts) {
      if (e.triggered) continue;
      const dist = Vec3.distance(playerPos, e.pos);
      if (dist <= e.radius) {
        e.triggered = true;
        e.revealed = true;
        if (!e.node.active) {
          e.node.active = true;
        }
        GlobalEventBus.emit('extract/available', { at: e.pos.clone() });
        if (typeof console !== 'undefined' && console.debug) {
          const label = e.kind === 'hidden' ? 'discover' : 'arrive';
          console.debug('[EXTRACT]', label, 'at', `(${e.pos.x.toFixed(1)},${e.pos.y.toFixed(1)})`);
        }
      }
    }
  };
}

export default ExtractSpawner;
